﻿using System;
using System.Text;
using System.Runtime.Serialization;
using System.Reflection;  //反射

using IcbcOpenDemo.Models.Tools;
using System.Web;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
namespace IcbcOpenDemo.Models
{
    /// <summary>
    /// 开放平台Url请求公共部份
    /// </summary>
    //[Serializable]
    public class RequestCommon
    {
        #region 公共参数和构造函数
        const string FORMAT = "json";
        const string CHARSET = "UTF-8";
        const string SIGNTYPE = "sm2";

        private string _appID;
        private string _apiServiceUrl;
        private string _privateKey;
        private BaseAPI _api;

        public RequestCommon(string appID, string serviceUrl, string privateKey, BaseAPI api)
        {
            _appID = appID;
            _apiServiceUrl = serviceUrl;
            _privateKey = privateKey;
            _api = api;

            timestamp = SeqNo.TimeStamp;
            msg_id = SeqNo.Msg_ID;

            // 在构造函数上初始化paramsDictionary，防止空引用异常
            paramsDictionary = CreateParamDictionary();
        }
        #endregion

        #region 通用请求参数
        /// <summary>
        /// 业务合作协议签订后由工行在开放平台后分配，并交给客户保管
        /// </summary>
        public string app_id { get { return _appID; } }

        /// <summary>
        /// 消息通讯唯一编号，每次调用独立生成，APP级唯一，
        /// 可自编，保证整个APP唯一
        /// </summary>
        public string msg_id { get; set; }

        //请求参数格式，仅支持json
        public string format { get { return FORMAT; } }

        //字符集 ,缺省为UTF-8
        public string charset { get { return CHARSET; } }

        //现在仅支持AES，部分接口支持加密，如接口无需加密，参数中此字段无需上送
        public string encrypt_type { get; set; }

        //签名类型，CA-工行颁发的证书认证，RSA-RSAWithSha1，RSA2-RSAWithSha256，缺省为RSA,本Demo使用RSA2
        public string sign_type { get { return SIGNTYPE; } }

        //报文签名，biz_content的RSA2加密后的字符串
        public string sign
        { get; set; }
        //get { return RSAClass.RSAFromPkcs8.signWithRSASHA256(GetStrToSign(), _privateKey, CHARSET); }}

        //交易发生时间戳，yyyy-MM-dd HH:mm:ss格式
        public string timestamp { get; set; }

        //采用ca认证方式时，需上送证书
        public string ca { get; set; }

        //请求参数的集合，JSON字符串
        public string biz_content { get { return _api.ToJsonString(); } }
        #endregion

        /// <summary>
        /// 将请求参数的转换成字典
        /// </summary>
        private Dictionary<string, string> paramsDictionary;

        /// <summary>
        /// 返回提交的完整网址
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();

            builder.Append(this.ActionString());
            builder.Append("?");
            builder.Append(this.PostContent());
            return builder.ToString();

        }

        /// <summary>
        /// 返回接口完整路径（包含API平台地址）
        /// </summary>
        /// <returns></returns>
        public string ActionString()
        {
            return string.Format("{0}{1}", _apiServiceUrl, _api.GetPath());
        }

        /// <summary>
        /// 返回待POST的数据,Url问号?后的字符串
        /// </summary>
        /// <param name="reqData"></param>
        /// <returns></returns>
        public string PostContent(bool urlEndoding = false)
        {
            //StringBuilder builder = new StringBuilder(@"/api/" + _api.GetPath() + "?");
            StringBuilder builder = new StringBuilder();

            // 设置
            paramsDictionary["sign"] = RSAClass.RSAFromPkcs8.signWithRSASHA256(GetStrToSign(), _privateKey, CHARSET);

            builder.Append(KeyValuePairToString(paramsDictionary, urlEndoding));
            return builder.ToString();
        }

        /// <summary>
        /// 返回待签名的字符串（除sign外所有参数，按首字母ASCII排序，待签名字符串不需UrlEncode）
        /// </summary>
        /// <returns></returns>
        public string GetStrToSign(bool urlEncoding = false)
        {
            // 剔除sign属性，其他属性按首字母排序
            IEnumerable<KeyValuePair<string, string>> orderedParam = paramsDictionary.Where(s => s.Key != "sign").OrderBy(o => o.Key);
            StringBuilder builder = new StringBuilder(@"/api/" + _api.GetPath() + "?");
            builder.Append(KeyValuePairToString(orderedParam));
            return builder.ToString();
        }

        /// <summary>
        /// 将KeyValuePair转换成Url参数形式key=value
        /// </summary>
        /// <param name="param">IEnumerable<KeyValuePair<string, string>>的集合</param>
        /// <param name="urlEncoding">是否进行Url编码</param>
        /// <returns>key1=value1&key2=value2...格式字符串</returns>
        private string KeyValuePairToString(IEnumerable<KeyValuePair<string, string>> param, bool urlEncoding = false)
        {
            //StringBuilder builder = new StringBuilder();
            List<string> list = new List<string>();
            // 将各属性生成key=value形式
            foreach (var k in param)
            {
                //builder.Append(string.Format(@"{0}={1}", k.Key, urlEncoding ? HttpUtility.UrlEncode(k.Value, Encoding.GetEncoding(charset)) : k.Value));
                list.Add(string.Format(@"{0}={1}", k.Key, urlEncoding ? HttpUtility.UrlEncode(k.Value, Encoding.GetEncoding(charset)) : k.Value));
            }

            return string.Join("&", list);
            // 去掉最后的&
            //return builder.ToString().Substring(0, builder.ToString().Length - 1);
        }

        /// <summary>
        /// 将各属性转成参数字符串字典，(属性名,属性值)
        /// </summary>
        /// <param name="par"></param>        
        private Dictionary<string, string> CreateParamDictionary() //IEnumerable<PropertyInfo> par)
        {
            Type t = typeof(RequestCommon);
            IEnumerable<PropertyInfo> par = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);

            //StringBuilder builder = new StringBuilder();
            Dictionary<string, string> output = new Dictionary<string, string>();
            foreach (PropertyInfo pi in par)
            {
                // 剔除null值的参数
                string piValue = this.GetType().GetProperty(pi.Name).GetValue(this, null) as string;

                if (piValue != null)
                {
                    //builder.Append(pi.Name);
                    //builder.Append("=");                    
                    //builder.Append(piValue);
                    //builder.Append("&");
                    output.Add(pi.Name, piValue);
                }
            }
            return output;
        }
    }
}
